/**
*@file       pnsession.cpp
*@brief      session class impl
*
*@author     graywind
*@version    1.0.0.0
*@data       2011/11/16
*/
#include "../include/pnsession.h"
#include "../include/interlock.h"

using namespace pnNetlib;

pnSession::pnSession(int id, boost::asio::io_service& ioService, long buffer_length):
m_bIsAlive(false),
m_eType(Usage_Invalid),
m_iID(id),
m_iState(pnSession::wait_send),
m_socket(ioService),
m_readbuffer(buffer_length),
m_writebuffer(buffer_length),
m_msgProc(0)
{

}

/***************************************************************************************************************************/
void
pnSession::close()
{
	if(is_open() == true)
	{
		try
		{
			m_socket.close();
		}
		catch(std::exception& e)
		{

		}

        m_bIsAlive = false;

		if(m_msgProc != 0)
		{
			if(m_eType == Usage_Listen)
				(*m_msgProc)(get_id(), 0, 0, 0, Kick, 0);
			else
				(*m_msgProc)(get_id(), 0, 0, 0, Disconnect, 0);
		}

         m_eType = Usage_Invalid;
	
		if(m_closeProc != 0)
			m_closeProc();
	}
}

pnSession::~pnSession()
{
	m_socket.close();
}

/***************************************************************************************************************************/
void
pnSession::read_body(unsigned long msg_id, const boost::system::error_code& ec, std::size_t bytes_transferred)
{
	if(!ec)
	{
		m_readbuffer.commit_buffer(bytes_transferred);
		if(m_msgProc != 0)
		{
			boost::asio::mutable_buffers_1 buffer = m_readbuffer.get_read_buffer(bytes_transferred);
			if(boost::asio::buffer_size(buffer) != 0)
				(*m_msgProc)(get_id(), boost::asio::buffer_cast<char*>(buffer), msg_id, bytes_transferred, Read, ec.value());
		}

		m_readbuffer.cast_buffer(bytes_transferred);

		start();
	}
	else
	{
		if(m_msgProc != 0)
			(*m_msgProc)(get_id(), 0, 0, 0, Read, ec.value());

		close();
	}
}

/***************************************************************************************************************************/
void
pnSession::read_header(const boost::system::error_code& ec, std::size_t bytes_transferred)
{
	if(!ec)
	{
		m_readbuffer.commit_buffer(bytes_transferred);
		stPnMsgHeader header;
		if(m_readbuffer.read_from_buffer((char*)&header, pnNetlib::header_size)  == true)
		{
			if(header.ulMsgLength > 0)
			{
				boost::asio::mutable_buffers_1 buffer = m_readbuffer.get_write_buffer(header.ulMsgLength);
			    if(boost::asio::buffer_size(buffer) != 0)
					m_socket.async_read_some(buffer, boost::bind(&pnSession::read_body, shared_from_this(), header.ulMsgID, boost::asio::placeholders::error,
			                                                                                                            boost::asio::placeholders::bytes_transferred));
			}
			else
			{
				if(m_msgProc != 0)
					(*m_msgProc)(get_id(), 0, header.ulMsgID, 0, Read, ec.value());

				start();
			}
		}
	}
	else
	{
		if(m_msgProc != 0)
			(*m_msgProc)(get_id(), 0, 0, 0, Read, ec.value());

		close();
	}
}

/***************************************************************************************************************************/
void
pnSession::start()
{
	boost::asio::mutable_buffers_1 buffer = m_readbuffer.get_write_buffer(pnNetlib::header_size);
	if(boost::asio::buffer_size(buffer) != 0)
		m_socket.async_read_some(buffer, boost::bind(&pnSession::read_header, shared_from_this(), boost::asio::placeholders::error,
			                                                                        boost::asio::placeholders::bytes_transferred) );
}

/***************************************************************************************************************************/
void
pnSession::start(eUsageType type)
{
    m_eType = type;
    m_bIsAlive = true;
    start();
}

/***************************************************************************************************************************/
void
pnSession::connect_handler(const boost::system::error_code& ec)
{
	if(!ec)
		start(Usage_Connect);
	else
		close();

    if(m_msgProc != 0)
		(*m_msgProc)(get_id(), 0, 0, 0, Connect, ec.value());
}

/***************************************************************************************************************************/
void
pnSession::connect(const std::string& ip, unsigned short port)
{
	boost::asio::ip::tcp::endpoint endpoint(
		                                     boost::asio::ip::address::from_string(ip), 
											 port
										    );

	m_socket.async_connect(
		                   endpoint,
						   	boost::bind(
		                                &pnSession::connect_handler,
                                        this, 
				                        boost::asio::placeholders::error)
						   );
}

/***************************************************************************************************************************/
bool
pnSession::send_msg(long bytes_len)
{
	boost::asio::mutable_buffers_1 buffer = m_writebuffer.get_read_buffer(bytes_len);
	if(boost::asio::buffer_size(buffer) != 0)
	{
			boost::asio::async_write(m_socket, buffer, boost::bind(&pnSession::send_handler, shared_from_this(), boost::asio::placeholders::error,
				                                                                                     boost::asio::placeholders::bytes_transferred));

			return true;
	}

	return false;
}

/***************************************************************************************************************************/
bool
pnSession::push_msg(stPnMsgHeader* pHeader, void* pMsg)
{
	long buffer_length = pnNetlib::header_size + pHeader->ulMsgLength;
	boost::asio::mutable_buffers_1 buffer = m_writebuffer.get_write_buffer(buffer_length);

	bool result = false;
	if(boost::asio::buffer_size(buffer) != 0)
	{
		memcpy(boost::asio::buffer_cast<char*>(buffer), pHeader, pnNetlib::header_size);
		memcpy(boost::asio::buffer_cast<char*>(buffer + pnNetlib::header_size), pMsg, pHeader->ulMsgLength);
		m_writebuffer.commit_buffer(buffer_length);

		result = true;
	}

	boost::lock_guard<boost::mutex>    lock(m_mutex);

	if(result == true)
	{
		if(m_iState == pnSession::wait_send)
		{
			m_iState = pnSession::proc_send;
			if(send_msg(buffer_length) == false)
				m_iState = pnSession::wait_send;
		}
		else
			m_validLength.push(buffer_length);
	}

	return result;
}

/***************************************************************************************************************************/
void
pnSession::send_handler(const boost::system::error_code& ec, std::size_t bytes_transferred)
{
	if(m_msgProc != 0)
		(*m_msgProc)(get_id(), 0, 0, 0, Send, ec.value());

	if(!ec)
	{
		m_writebuffer.cast_buffer(bytes_transferred);


		boost::lock_guard<boost::mutex>    lock(m_mutex);

		if(m_validLength.size() > 0)
		{
			if(send_msg(m_validLength.front()) == true)
				m_validLength.pop();
		}
		else
			m_iState = pnSession::wait_send;
	}
	else
		close();
}

/***************************************************************************************************************************/
void
pnSession::set_msg_proc(MsgProc msgProc)
{
	m_msgProc = msgProc;
}

/***************************************************************************************************************************/
void
pnSession::set_close_proc(boost::function<void(void)>   f)
{
	m_closeProc = f;
}